﻿using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;

namespace WaterRegimenMonitor.Common
{
    /// <summary>
    /// 密码哈希辅助类。
    /// </summary>
    public static class PasswordHash
    {
        private const int PASSWORDSALTLENGTH = 16;

        /// <summary>
        /// 获得以Base64编码的已哈希密码字符串。
        /// </summary>
        /// <param name="password">要哈希的密码字符串。</param>
        /// <returns></returns>
        public static string GetHashedPasswordBase64(string password)
        {
            byte[] hashedPasswordBytes = GetHashedPasswordBytes(password);
            return Convert.ToBase64String(hashedPasswordBytes);
        }

        public static bool Compare(string password, string passwordHashBase64)
        {
            byte[] hash = Convert.FromBase64String(passwordHashBase64);
            return ComparePasswordHash(password, hash);
        }

        private static byte[] GetHashedPasswordBytes(string password)
        {
            var salt = GetRandomBytes(PASSWORDSALTLENGTH);
            var enteredPassword = Encoding.UTF8.GetBytes(password);

            var passwordByteList = new List<byte>();
            passwordByteList.AddRange(salt);
            passwordByteList.AddRange(enteredPassword);

            byte[] passwordHashResult;
            using (SHA256 sha256 = new SHA256Managed())
            {
                passwordHashResult = sha256.ComputeHash(passwordByteList.ToArray());
            }

            passwordByteList.Clear();
            passwordByteList.AddRange(salt);
            passwordByteList.AddRange(passwordHashResult);

            return passwordByteList.ToArray();
        }

        private static byte[] GetRandomBytes(int size)
        {
            var random = new Byte[size];
            var provider = new RNGCryptoServiceProvider();
            provider.GetNonZeroBytes(random);
            return random;
        }

        private static bool ComparePasswordHash(string password, byte[] passwordHash)
        {
            //判断是否指定了密码
            if (string.IsNullOrEmpty(password))
            {
                //如果没有指定，则认为不相等
                return false;
            }

            bool isValidated = false;
            var salt = new byte[PASSWORDSALTLENGTH];
            var hash = new byte[passwordHash.Length - PASSWORDSALTLENGTH];

            Array.Copy(passwordHash, salt, salt.Length);
            Array.Copy(passwordHash, PASSWORDSALTLENGTH, hash, 0, hash.Length);

            var enteredPassword = Encoding.UTF8.GetBytes(password);
            var passwordByteList = new List<byte>();
            passwordByteList.AddRange(salt);
            passwordByteList.AddRange(enteredPassword);

            byte[] passwordHashResult;
            using (SHA256 sha256 = new SHA256Managed())
            {
                passwordHashResult = sha256.ComputeHash(passwordByteList.ToArray());
            }

            if (CompareBytes(passwordHashResult, hash))
                isValidated = true;

            return isValidated;
        }

        private static bool CompareBytes(byte[] left, byte[] right)
        {
            if (left.Length != right.Length)
                return false;

            for (int i = 0; i < left.Length; i++)
                if (left[i] != right[i])
                    return false;

            return true;
        }
    }

}
